// compile:
// $ g++ -o abcd abcd.cpp -nostdlib -ffreestanding -fno-rtti
//
// license: cc0 1.0 (public domain)

#include <stdarg.h>
#include <stdint.h>

using size_t = unsigned long;

#ifdef __x86_64__
#    define GET_BP(base) __asm__("mov %%rbp, %0\n" : "=r"(base));
#    define SET_STACK(base, size) \
        __asm__("mov %0, %%rsp\n" ::"r"(&base[size - 1]));
#elif __i386__
#    define GET_BP(base) __asm__("mov %%ebp, %0\n" : "=r"(base));
#    define SET_STACK(base, size) \
        __asm__("mov %0, %%esp\n" ::"r"(&base[size - 1]));
#else
#    error "Architecture not supported"
#endif

const auto strlen = [](const char* str) -> const size_t {
    const char* orig_ptr = str;
    while (*str++)
        ;
    return str - orig_ptr;
};

const auto not_itoa = [](int num, uint8_t base, bool buffcase) -> char* {
    int         i          = 30;
    static char buffer[32] = {0};

    if (num == 0) {
        buffer[0] = '0';
        return buffer;
    }

    while (num && i) {
        buffer[i] = buffcase ? "0123456789ABCDEF"[num % base]
                             : "0123456789abcdef"[num % base];
        num /= base;
        i--;
    }
    return &buffer[i + 1];
};

namespace platform
{
    using fd_t = unsigned int;

    struct base_platform_controller
    {
        virtual void output(fd_t fd, const char chr)  = 0;
        virtual void output(fd_t fd, const char* str) = 0;
        virtual void stop(int exit_code = 0)          = 0;
    };

    namespace syscall
    {
        inline int write(fd_t fd, const char* buffer, int size)
        {
            int written;
#if __x86_64__
            __asm__("syscall\n"
                    : "=a"(written)
                    : "a"(1), "b"(fd), "m"(buffer), "d"(size));
#elif __aarch64__
            __asm__(
                "ldr x0, %0\n"
                "ldr x1, %1\n"
                "mov w8, #64\n"
                "svc #0\n" ::"m"(fd),
                "m"(buffer), "m"(size)
            );
#else
#    error "Unsupported architecture"
#endif
            return written;
        }

        inline void exit(int exit = 0)
        {
#if __x86_64__
            __asm__("syscall\n" ::"a"(60), "b"(exit));
#elif __aarch64__
#    warning \
        "No exit() syscall was defined for this architecture yet. Expect segfaults"
#else
#    error "Unsupported architecture"
#endif
        }

        struct platform_controller : base_platform_controller
        {
            void output(fd_t fd, const char chr) override
            {
                syscall::write(fd, &chr, 1);
            }

            void output(fd_t fd, const char* str) override
            {
                syscall::write(fd, str, strlen(str));
            }

            void stop(int exit_code = 0) override { syscall::exit(exit_code); }
        };
    }

    syscall::platform_controller pc;
}

namespace io
{
    using namespace ::platform;

    enum std_fd : fd_t
    {
        STDOUT = 1,
        STDERR,
        STDIN,
    };

    struct base_output
    {
        virtual inline void put(const char chr) const  = 0;
        virtual inline void put(const char* str) const = 0;
        virtual inline void put() const                = 0;
    };

    namespace printf
    {
        enum
        {
            PRINTF_TYPE_CHAR = 0x00000010,
            PRINTF_TYPE_STR  = 0x00000020,
            PRINTF_TYPE_INT  = 0x00000030,
            PRINTF_TYPE_UINT = 0x00000040,
            PRINTF_BASE_DEC  = 0x0000A000,
            PRINTF_BASE_HEX  = 0x0000F000,
            PRINTF_MASK_TYPE = 0x000000F0,
            PRINTF_MASK_CASE = 0x00000100,
            PRINTF_MASK_BASE = 0x0000F000
        };

        void vaprintf(va_list* args, const char* format, const base_output& out)
        {
            const auto vaprintf_parse_args = [](const char* format
                                             ) -> uint32_t {
                uint32_t flags = 0;

                if (*format == '0')
                    format++;

                if (*format == 'c')
                    flags |= PRINTF_TYPE_CHAR;
                else if (*format == 's')
                    flags |= PRINTF_TYPE_STR;
                else if (*format == 'd') {
                    flags |= PRINTF_TYPE_INT;
                    flags |= PRINTF_BASE_DEC;
                } else if (*format == 'X') {
                    flags |= PRINTF_MASK_CASE;
                    flags |= PRINTF_TYPE_UINT;
                    flags |= PRINTF_BASE_HEX;
                } else if (*format == 'x') {
                    flags |= PRINTF_TYPE_UINT;
                    flags |= PRINTF_BASE_HEX;
                }

                format++;
                return flags;
            };

            const auto vaprintf_handle_args = [vaprintf_parse_args](
                                                  va_list*           args,
                                                  const char*        format,
                                                  const base_output& out
                                              ) {
                uint32_t flags = vaprintf_parse_args(format);

                int  base        = (flags & PRINTF_MASK_BASE) >> 12;
                bool buffer_case = flags & PRINTF_MASK_CASE;

                /* 16 in flags becomes 15, so we need to set 16 */
                base = base == 15 ? 16 : base;

                if ((flags & PRINTF_MASK_TYPE) == PRINTF_TYPE_CHAR)
                    out.put(va_arg(*args, int));
                else if ((flags & PRINTF_MASK_TYPE) == PRINTF_TYPE_STR)
                    out.put(va_arg(*args, const char*));
                else if ((flags & PRINTF_MASK_TYPE) == PRINTF_TYPE_UINT)
                    out.put(not_itoa(va_arg(*args, uint32_t), base, buffer_case)
                    );
                else if ((flags & PRINTF_MASK_TYPE) == PRINTF_TYPE_INT)
                    out.put(not_itoa(va_arg(*args, int), base, buffer_case));
            };

            while (*format) {
                if (*format == '%') {
                    format++;
                    vaprintf_handle_args(args, format, out);
                } else {
                    out.put(*format);
                }
                format++;
            }
        }

        void out_printf(const base_output& out, const char* format, ...)
        {
            va_list va;

            va_start(va, format);
            vaprintf(&va, format, out);
            va_end(va);
        }
    }

    template<fd_t fd> struct fd_output : base_output
    {
        inline void put(const char chr) const override
        {
            platform::pc.output(fd, chr);
        }

        inline void put(const char* str) const override
        {
            platform::pc.output(fd, str);
        }

        inline void put() const override { platform::pc.output(fd, "\n"); }
    };

    template<typename O> struct formatted_output
    {
        const O output;

        inline void put(const char chr) const { output.put(chr); }

        inline void put(const char* format, ...) const
        {
            va_list va;

            va_start(va, format);
            io::printf::vaprintf(&va, format, output);
            va_end(va);
        }

        inline void put(const bool boolean) const
        {
            output.put(boolean ? "(true)" : "(false)");
        }

        inline void put() const { output.put(); }
    };
}

namespace error
{
    const io::formatted_output<io::fd_output<io::STDERR>> err;

    struct stack_slice
    {
        stack_slice* bp;
        uintptr_t    ip;

        inline void print() { err.put(" - BP = 0x%x; IP = 0x%x\n", bp, ip); }

        static inline void trace(int trace_limit)
        {
            stack_slice* base;
            GET_BP(base);

            err.put("stacktrace:\n");
            for (int count = 0; base && count < trace_limit; count++) {
                base->print();
                base = base->bp;
            }
        }

        static inline void trace()
        {
            stack_slice* base;
            GET_BP(base);

            err.put("stacktrace:\n");
            for (int count = 0; base && count < 100; count++) {
                base->print();
                base = base->bp;
            }
        }
    };

    struct error_manager
    {
        inline void exit_with_message(const char* str, int code = 0) const
        {
            err.put(str);
            err.put();
            stack_slice().trace();
            this->exit(code);
        }

        inline void print(const char* str) const
        {
            err.put(str);
            err.put();
            stack_slice().trace();
        }

        inline void exit() const { platform::pc.stop(0); }

        inline void exit(int code = 0) const { platform::pc.stop(code); }
    };

    struct error
    {
        const char*         message;
        const error_manager em;

        explicit error(const char* msg)
            : message(msg),
              em()
        {
        }

        inline void issue() const { em.exit_with_message(message, 1); }

        inline void print() const { em.print(message); }
    };
}

namespace containers
{
    template<size_t S> struct bounds_checker
    {
        [[nodiscard]] inline bool check(size_t index, bool strict = true) const
        {
            if (index >= S) {
                if (strict)
                    error::error("Caught potential OOB situation").issue();
                else
                    return false;
            } else {
                return true;
            }

            return false;
        }
    };

    template<typename T> struct iterable
    {
        virtual inline const T*                   begin() const  = 0;
        virtual inline const T*                   end() const    = 0;
        [[nodiscard]] virtual inline const size_t length() const = 0;
    };

    template<typename T> struct base_array : iterable<T>
    {
        [[nodiscard]] virtual inline const T get(const uintptr_t index
        ) const = 0;
        [[nodiscard]] virtual inline const T operator[](const size_t index
        ) const = 0;
    };

    template<typename T, size_t S> class array : base_array<T>
    {
    private:
        T                 contents[S];
        bounds_checker<S> bc;

    public:
        explicit array(const T* contents)
        {
            for (size_t i = 0; i < S && bc.check(i); i++)
                this->contents[i] = contents[i];
        }

        [[nodiscard]] inline const T get(uintptr_t index) const override
        {
            if (bc.check(index))
                return this->contents[index];
            else
                return -1;
        }

        [[nodiscard]] inline const T operator[](const size_t index
        ) const override
        {
            return this->get(index);
        };

        inline void set(uintptr_t index, const T& data)
        {
            if (bc.check(index))
                this->contents[index] = data;
        }

        [[nodiscard]] inline const size_t length() const override { return S; }

        inline const T* begin() const override { return &this->contents[0]; };

        inline const T* end() const override { return &this->contents[S]; };
    };

    class string : base_array<char>
    {
    private:
        char                 str[8192];
        bounds_checker<8192> bc;

    public:
        explicit string()
            : str()
        {
        }

        explicit string(const char* str)
        {
            for (size_t i = 0; i < strlen(str) && bc.check(i); i++)
                this->str[i] = str[i];
        }

        [[nodiscard]] inline const char* cstr() const { return this->str; }

        inline const char* begin() const override { return this->str; }

        inline const char* end() const override
        {
            return &this->str[this->length() - 1];
        }

        [[nodiscard]] inline const size_t length() const override
        {
            return strlen(this->str);
        }

        [[nodiscard]] inline const char get(const uintptr_t index
        ) const override
        {
            if (bc.check(index) && index < this->length())
                return this->str[index];
            else
                return 0;
        }

        [[nodiscard]] inline const char operator[](const size_t index
        ) const override
        {
            return this->get(index);
        }
    };

    template<typename A, typename B> struct pair
    {
        A first;
        B second;
    };
}

namespace cpp_runtime
{
    extern "C" void __stack_chk_fail()
    {
        error::error("Stack smashed XwX").issue();
    }

    extern "C" void __cxa_pure_virtual()
    {
        error::error("Pure virtual function cannot be called XwX").print();
    }
}

namespace program
{
    error::stack_slice stack[8192] {0};

    struct combined_output
    {
        const io::formatted_output<io::fd_output<io::STDOUT>> stdout;
        const io::formatted_output<io::fd_output<io::STDERR>> stderr;

        inline void put(const char chr) const { stdout.put(chr); }

        inline void put(const char* format, ...) const
        {
            va_list va;

            va_start(va, format);
            io::printf::vaprintf(&va, format, stdout.output);
            va_end(va);
            stdout.put(format);
        }

        inline void put(const bool boolean) const
        {
            stdout.put(boolean ? "(true)" : "(false)");
        }

        inline void put() const { stdout.put(); }

        inline void err_put(const char chr) const { stderr.put(chr); }

        inline void err_put(const char* format, ...) const
        {
            va_list va;

            va_start(va, format);
            io::printf::vaprintf(&va, format, stderr.output);
            va_end(va);
        }

        inline void err_put(const bool boolean) const
        {
            stderr.put(boolean ? "(true)" : "(false)");
        }

        inline void err_put() const { stderr.put(); }
    };

    struct base_program
    {
        const combined_output out;

        [[nodiscard]] virtual inline const int main() = 0;
    };

    struct prog : base_program
    {
        void print_string(const containers::string& str)
        {
            for (const auto& x : str)
                out.put(x);
            out.put();
        }

        [[nodiscard]] inline const int main() override
        {
            print_string(containers::string("Hello, world!"));

            return 0;
        }
    };
}

extern "C" void _start()
{
    // setup stack properly, for safety!
    SET_STACK(program::stack, 8191);

    platform::pc.stop(program::prog().main());
}
